package com.pactera.asmp.server.service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.pactera.asmp.server.common.constants.ResultCode;
import com.pactera.asmp.server.common.exception.CustomException;
import com.pactera.asmp.server.common.utils.JwtTokenUtil;
import com.pactera.asmp.server.dao.RoleMapper;
import com.pactera.asmp.server.dao.UserMapper;
import com.pactera.asmp.server.dao.UserProjectMapper;
import com.pactera.asmp.server.dao.UserRoleMapper;
import com.pactera.asmp.server.entity.UserRoleDTO;
import com.pactera.asmp.server.entity.UserRolesVO;
import com.pactera.asmp.server.entity.UserSearchDTO;
import com.pactera.asmp.server.pojo.Role;
import com.pactera.asmp.server.pojo.User;
import com.pactera.asmp.server.pojo.UserProject;
import com.pactera.asmp.server.pojo.UserProjectKey;
import com.pactera.asmp.server.pojo.UserRoleKey;
import com.pactera.asmp.server.shiro.ShiroRealm;
import com.pactera.asmp.server.utils.Base64Util;
import com.pactera.asmp.server.utils.DateUtil;
import com.pactera.asmp.server.utils.ExcelUtils;
import com.pactera.asmp.server.utils.PageDataResult;

@Service
public class UserServiceImpl implements UserService {
	private static final Logger logger = LoggerFactory
			.getLogger(UserServiceImpl.class);
	@Autowired
	private UserMapper userMapper;
	@Autowired
	private RoleMapper roleMapper;
	@Autowired
	private UserRoleMapper userRoleMapper;
    @Autowired
    private UserProjectMapper userProjectMapper;
	private static final String HEADER = "ID,用户名,手机号,邮箱,角色名称,创建时间";

	@Override
	public PageDataResult getUsers(UserSearchDTO userSearch, int page, int limit) {
		// 时间处理
		if (null != userSearch) {
			if (StringUtils.isNotEmpty(userSearch.getInsertTimeStart())
					&& StringUtils.isEmpty(userSearch.getInsertTimeEnd())) {
				userSearch.setInsertTimeEnd(DateUtil.format(new Date()));
			} else if (StringUtils.isEmpty(userSearch.getInsertTimeStart())
					&& StringUtils.isNotEmpty(userSearch.getInsertTimeEnd())) {
				userSearch.setInsertTimeStart(DateUtil.format(new Date()));
			}
			if (StringUtils.isNotEmpty(userSearch.getInsertTimeStart())
					&& StringUtils.isNotEmpty(userSearch.getInsertTimeEnd())) {
				if (userSearch.getInsertTimeEnd().compareTo(
						userSearch.getInsertTimeStart()) < 0) {
					String temp = userSearch.getInsertTimeStart();
					userSearch
							.setInsertTimeStart(userSearch.getInsertTimeEnd());
					userSearch.setInsertTimeEnd(temp);
				}
			}
		}
		PageDataResult pdr = new PageDataResult();
		PageHelper.startPage(page, limit);
		List<UserRoleDTO> urList = userMapper.getUsers(userSearch);
		// 获取分页查询后的数据
		PageInfo<UserRoleDTO> pageInfo = new PageInfo<>(urList);
		// 设置获取到的总记录数total：
		pdr.setTotals(Long.valueOf(pageInfo.getTotal()).intValue());
		// 将角色名称提取到对应的字段中
		if (null != urList && urList.size() > 0) {
			for (UserRoleDTO ur : urList) {
				List<Role> roles = roleMapper.getRoleByUserId(ur.getId());
				if (null != roles && roles.size() > 0) {
					StringBuilder sb = new StringBuilder();
					StringBuilder idSb = new StringBuilder();
					for (int i = 0; i < roles.size(); i++) {
						Role r = roles.get(i);
						sb.append(r.getRoleName());
						idSb.append(r.getId());
						if (i != (roles.size() - 1)) {
							sb.append("，");
							idSb.append(",");
						}
					}
					ur.setRoleIds(idSb.toString());
					ur.setRoleNames(sb.toString());
				}
			}
		}
		pdr.setList(urList);
		return pdr;
	}

	@Override
	@Transactional
	public String setDelUser(Integer id, Integer isDel, Integer insertUid,
			Integer version, HttpServletRequest request) {
		User dataUser = this.userMapper.selectByPrimaryKey(id);
		// 版本不一致
		if (null != dataUser && null != dataUser.getVersion() && !String.valueOf(version).equals(String.valueOf(dataUser.getVersion()))) {
			return "操作失败，请您稍后再试";
		}
		userRoleMapper.deleteByUserId(id);
		int count = this.userMapper.setDelUser(id, isDel, insertUid, LocalDateTime.now());
		if(count == 1) {
//		    if(isDel == 1) {
//    	        RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils
//    	                .getSecurityManager();
//    	        ShiroRealm authRealm = (ShiroRealm) rsm.getRealms().iterator()
//    	                .next();
//                authRealm.clearCachedAuth();
//                authRealm.clearToken(request.getHeader(JwtTokenUtil.AUTH_HEADER_KEY));
//		    }
	        return "ok";
		}
        return "删除失败，请您稍后再试";
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 30000, rollbackFor = {
			RuntimeException.class, Exception.class })
	public String setUser(User user, List<Integer> roleIds) throws Exception {
		int userId;
		if (user.getId() != null) {
			// 判断用户是否已经存在
			User existUser = this.userMapper.findUserByMobile(user.getMobile());
			if (null != existUser
					&& !String.valueOf(existUser.getId()).equals(
							String.valueOf(user.getId()))) {
				return "该手机号已经存在";
			}
			User exist = this.userMapper.findUserByName(user.getUsername());
			if (null != exist
					&& !String.valueOf(exist.getId()).equals(
							String.valueOf(user.getId()))) {
				return "用户名称已存在，请重新输入";
			}
            if(StringUtils.isNotEmpty(user.getEmail())) {
                List<User> emailExists = this.userMapper.findUserByEmail(user.getEmail());
                if(CollectionUtils.isNotEmpty(emailExists)
                        && emailExists.stream().noneMatch(emailExist -> user.getId().intValue() == emailExist.getId().intValue())) {
                    return "该邮箱已经存在";
                }
            }
			User dataUser = this.userMapper.selectByPrimaryKey(user.getId());
			// 版本不一致
			if (null != dataUser
					&& null != dataUser.getVersion()
					&& !String.valueOf(user.getVersion()).equals(
							String.valueOf(dataUser.getVersion()))) {
				return "操作失败，请您稍后再试";
			}
			// 更新用户
			userId = user.getId();
			user.setUpdateTime(LocalDateTime.now());
			// 设置加密密码
			if (StringUtils.isNotBlank(user.getPassword())) {
				user.setPassword(DigestUtils.md5Hex(Base64Util.decodePwd(user.getPassword())));
//                user.setPassword(DigestUtils.md5Hex(user.getPassword()));
			}
			this.userMapper.updateByPrimaryKeySelective(user);
			// 删除之前的角色
			List<UserRoleKey> urs = this.userRoleMapper.findByUserId(userId);
			if (null != urs && urs.size() > 0) {
				for (UserRoleKey ur : urs) {
					this.userRoleMapper.deleteByPrimaryKey(ur);
				}
			}
            // 清除ehcache中所有用户权限缓存，必须触发鉴权方法才能执行授权方法doGetAuthorizationInfo
            // 方案一【不推荐】：通过SessionDAO拿到所有在线的用户，Collection<Session> sessions =
            // sessionDAO.getActiveSessions();
            // 遍历找到匹配的，更新他的信息【不推荐，分布式或用户数量太大的时候，会有问题。】；
            // 方案二【推荐】：用户信息价格flag（或version）标记，写个拦截器，每次请求判断flag（或version）是否改动，如有改动，请重新登录或自动更新用户信息（推荐）；

            RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils
                    .getSecurityManager();
            ShiroRealm authRealm = (ShiroRealm) rsm.getRealms().iterator()
                    .next();
			// 如果是自己，修改完成之后，直接退出；重新登录
			User adminUser = (User) SecurityUtils.getSubject().getPrincipal();
			if (adminUser != null
					&& adminUser.getId().intValue() == user.getId().intValue()) {
				logger.debug("更新自己的信息，退出重新登录！adminUser=" + adminUser);
				SecurityUtils.getSubject().logout();
				authRealm.clearCachedAuth();
			}else {
			    authRealm.clearAuthByUserId(userId, user.getUsername(), true, true);
			}
			
			logger.debug("清除所有用户权限缓存！！！");
		} else {
			// 判断用户是否已经存在
			User existUser = this.userMapper.findUserByMobile(user.getMobile());
			if (null != existUser) {
				return "该手机号已经存在";
			}
			User exist = this.userMapper.findUserByName(user.getUsername());
			if (null != exist) {
				return "用户名称已存在，请重新输入";
			}
            if(StringUtils.isNotEmpty(user.getEmail())) {
                List<User> emailExists = this.userMapper.findUserByEmail(user.getEmail());
                if(CollectionUtils.isNotEmpty(emailExists)) {
                    return "该邮箱已经存在";
                }
            }
			// 新增用户
			user.setInsertTime(LocalDateTime.now());
			user.setIsDel(false);
			// 设置加密密码
			if (StringUtils.isNotBlank(user.getPassword())) {
				user.setPassword(DigestUtils.md5Hex(Base64Util.decodePwd(user.getPassword())));
//			    user.setPassword(DigestUtils.md5Hex(user.getPassword()));
			} else {
				user.setPassword(DigestUtils.md5Hex("654321"));
			}
			this.userMapper.insert(user);
			userId = user.getId();
		}
		for (int roleId : roleIds) {
			UserRoleKey urk = new UserRoleKey();
			urk.setRoleId(roleId);
			urk.setUserId(userId);
			this.userRoleMapper.insert(urk);
		}
		return "ok";
	}


	@Override
	public UserRolesVO getUserAndRoles(Integer id) {
		return this.userMapper.getUserAndRoles(id);
	}

	@Override
	public User findUserByMobile(String mobile) {
		return this.userMapper.findUserByMobile(mobile);
	}

	@Override
	public User findUserByUserName(String userName) {
		return this.userMapper.findUserByName(userName);
	}


	@Override
	public int updatePwd(Integer id, String password) {
		return this.userMapper.updatePwd(id, password, LocalDateTime.now());
	}

	@Override
	public int setUserLockNum(Integer id, int isLock) {
		return this.userMapper.setUserLockNum(id, isLock);
	}

	@Override
	public User findUserById(Integer id) {
		return this.userMapper.findUserById(id);
	}

	/**
	 * 退出登录
	 */
    @Override
    public void logout(HttpServletRequest request) {
        RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils
                .getSecurityManager();
        ShiroRealm authRealm = (ShiroRealm) rsm.getRealms().iterator()
                .next();
        authRealm.clearCachedAuth();
        authRealm.clearToken(request.getHeader(JwtTokenUtil.AUTH_HEADER_KEY));
    }

    /**
     * 导出用户信息
     */
    @Override
    public void export(UserSearchDTO userSearch, HttpServletResponse response) {
        try {
            PageDataResult pdr = this.getUsers(userSearch, 1, Integer.MAX_VALUE);
            List<UserRoleDTO> urList = (List<UserRoleDTO>) pdr.getList();
            List<List<String>> dataList = Lists.newArrayList();
            if(CollectionUtils.isNotEmpty(urList)) {
                dataList.addAll(urList.stream().map(user -> {
                    List<String> data = new ArrayList<>();
                    data.add(String.valueOf(user.getId()));
                    data.add(user.getUsername());
                    data.add(user.getMobile());
                    data.add(user.getEmail());
                    data.add(user.getRoleNames());
                    data.add(user.getInsertTime());
                    return data;
                }).collect(Collectors.toList()));
            }
            ExcelUtils.writeExcel(response, Lists.newArrayList(HEADER.split(",")), dataList, "用户");
        }catch(Exception ex) {
            ExcelUtils.writeExcel(response, Lists.newArrayList(HEADER.split(",")), Lists.newArrayList(), "用户");
        }
    }

    @Override
    public List<Integer> getRelatedProject() {
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        if(null == user || user.getId() == null) {
            throw new CustomException(ResultCode.PERMISSION_EXPIRE);
        }
        List<Role> roles = roleMapper.getRoleByUserId(user.getId());
        if(roles.stream().anyMatch(role -> role.getLevel() == 0)) {
            return null;
        }
        List<UserProject> userProjects = userProjectMapper.findByUserId(user.getId());
        if(CollectionUtils.isEmpty(userProjects)) {
            throw new CustomException(ResultCode.BUSINESS_NO_RELATED_PROJECT);
        }
        return userProjects.stream().map(UserProjectKey::getProjectId).distinct().collect(Collectors.toList());
    }

}
